home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / source / scrasm / scroll.inc < prev    next >
Text File  |  1993-03-08  |  19KB  |  441 lines

  1. ;; Global variables used here ...
  2. EVEN
  3. ScrollPosX      dw      0       ; Scroll origin, upper-left X
  4. ScrollPosY      dw      0       ; Scroll origin, upper-left Y
  5. ScrollDX        dw      0       ; Amount to change scroll origin, X
  6. ScrollDY        dw      0       ; Amount to change scroll origin, Y
  7.  
  8. ;; SCROLL:
  9. ;; This routine takes care of all of the scrolling, however it calls
  10. ;; outside drawing routines to update the screen.  Scrollx and
  11. ;; Scrolly determine the amount to scroll by.
  12. ;; Note that this does only RELATIVE scrolling, not absolute scrolling.
  13. ;; Scroll saves time by updating only up to the one row or column of
  14. ;; tiles which have come into view due to a change in scroll offset.
  15. ;; In other words, it's not good for "jumping" to a particular point,
  16. ;; although this effect can be accomplished in other ways -- the draw_full
  17. ;; routine is available to draw a full screen again.
  18. ;; Sometimes this means that you will have to calculate values ahead of
  19. ;; time, for instance if you wish the scrolling to keep a certain sprite
  20. ;; in the center of the screen.  In this case, just set ScrollDX and
  21. ;; ScrollDY to the delta-x and delta-y of the sprite.
  22. ;; * Newly added:
  23. ;; Since there are three pages, it is necessary to keep each one of them
  24. ;; up to date with each scroll.  Recently, I was doing some fast (8+
  25. ;; pixels per frame) scrolling and noticed that there was a significant
  26. ;; pause when the screen snapped to a new origin.  (The origin is always
  27. ;; at a square's corner, even though it may not look like it because it
  28. ;; disguises things by smooth-panning the hardware.)  Every time it
  29. ;; scrolled, it was drawing the new information and copying it to the
  30. ;; two other planes.  I've now distributed the load over successive
  31. ;; pages, in other words it doesn't copy the new info all at once, but
  32. ;; over several frames.  This really smoothed out the scrolling so that
  33. ;; while there are still some jumps, they only occur very infrequently
  34. ;; and then only at 15 or 16 pixel/frame scroll rates...)  That's the
  35. ;; "catchup" code at the bottom, and that's why it's more complex than
  36. ;; it maybe could be...
  37. EVEN
  38. Scroll          PROC    near
  39.         ; Using the ScrollDX variable as delta-x, move the scroll-origin
  40.         ; in the x direction.  Then, if the visible screen is now
  41.         ; viewing invalid data, snap the origin to a new point and
  42.         ; draw any new columns that are necessary.
  43. do_x_scroll:    mov     ax,cs:ScrollPosX
  44.                 add     ax,cs:ScrollDX           ; ScrollDX is a delta-x
  45.                 jl      wrap_l                  ; wrap left if negative
  46.                 cmp     ax,VIRTUAL_WIDTH - SCREEN_WIDTH ; too far right?
  47.                 jge     wrap_r                  ; wrap right if too big
  48.                 mov     cs:ScrollPosX,ax        ; Stores new scroll-x
  49.         ; (just like above, for y:)
  50.         ; Using the ScrollDY variable as delta-y, move the scroll-origin
  51.         ; in the y direction.  Then, if the visible screen is now
  52.         ; viewing invalid data, snap the origin to a new point and
  53.         ; draw any new rows that are necessary.
  54. do_y_scroll:    mov     ax,cs:ScrollPosY
  55.                 add     ax,cs:ScrollDY          ; ScrollDY is a delta-y
  56.                 jl      wrap_t                  ; wrap top if negative
  57.                 cmp     ax,(VIRTUAL_HEIGHT - SCREEN_HEIGHT) * VIRTUAL_WIDTH
  58.                 jge     wrap_b                  ; wrap bottom if too big
  59.                 mov     cs:ScrollPosY,ax        ; Store the new scroll-y
  60.                 jmp     calculate
  61.  
  62.         ; To wrap to the right:
  63.         ; Add a square's width to the origin's upper left corner, and
  64.         ; subtract the same amount from the scroll origin's upper left
  65.         ; corner.  This makes no difference on the screen but allows
  66.         ; us to forget about the leftmost column on the screen (it's
  67.         ; offscreen now...) so we can take over the right column.
  68.         ; See any documentation I included for an explanation of the
  69. EVEN    ; scrolling...
  70. wrap_r:         add     cs:upper_left,SQUARE_WIDTH / 4
  71.                 sub     ax,SQUARE_WIDTH
  72.                 mov     cs:ScrollPosX,ax
  73.  
  74.                 mov     dx,MapInfo.Wid
  75.                 mov     bp,MapInfo.OffX1
  76.                 inc     bp
  77.                 cmp     bp,dx
  78.                 jb      wrap_r1_ok
  79.                 sub     bp,dx
  80. wrap_r1_ok:     mov     MapInfo.OffX1,bp
  81.  
  82.                 mov     bp,MapInfo.OffX2
  83.                 inc     bp
  84.                 cmp     bp,dx
  85.                 jb      wrap_r2_ok
  86.                 sub     bp,dx
  87. wrap_r2_ok:     mov     MapInfo.OffX2,bp
  88.  
  89.                 mov     bp,MapInfo.WrapX
  90.                 dec     bp
  91.                 jnz     wrap_r3_ok
  92.                 add     bp,dx
  93. wrap_r3_ok:     mov     MapInfo.WrapX,bp
  94.  
  95.                 call    update_right
  96.                 jmp     do_y_scroll     ; Jump back to do Y
  97.  
  98. EVEN    ; Same for left side
  99. wrap_l:         sub     cs:upper_left,SQUARE_WIDTH / 4
  100.                 add     ax,SQUARE_WIDTH
  101.                 mov     cs:ScrollPosX,ax
  102.  
  103.                 mov     dx,MapInfo.Wid
  104.                 mov     bp,MapInfo.OffX1
  105.                 dec     bp
  106.                 cmp     bp,dx
  107.                 jb      wrap_l1_ok
  108.                 add     bp,dx
  109. wrap_l1_ok:     mov     MapInfo.OffX1,bp
  110.  
  111.                 mov     bp,MapInfo.OffX2
  112.                 dec     bp
  113.                 cmp     bp,dx
  114.                 jb      wrap_l2_ok
  115.                 add     bp,dx
  116. wrap_l2_ok:     mov     MapInfo.OffX2,bp
  117.  
  118.                 mov     bp,MapInfo.WrapX
  119.                 inc     bp
  120.                 cmp     bp,dx
  121.                 jbe     wrap_l3_ok
  122.                 sub     bp,dx
  123. wrap_l3_ok:     mov     MapInfo.WrapX,bp
  124.  
  125.                 call    update_left
  126.                 jmp     do_y_scroll     ; Jump back to do Y
  127.  
  128. EVEN    ; Same for bottom
  129. wrap_b:         add     cs:upper_left,(SQUARE_HEIGHT * VIRTUAL_WIDTH) / 4
  130.                 sub     ax,SQUARE_HEIGHT * VIRTUAL_WIDTH
  131.                 mov     cs:ScrollPosY,ax
  132.  
  133.                 mov     bp,MapInfo.OffY1
  134.                 mov     dx,MapInfo.Extent
  135.                 add     bp,MapInfo.Wid
  136.                 cmp     bp,dx
  137.                 jb      wrap_b1_ok
  138.                 sub     bp,dx
  139. wrap_b1_ok:     mov     MapInfo.OffY1,bp
  140.  
  141.                 mov     bp,MapInfo.OffY2
  142.                 add     bp,MapInfo.Wid
  143.                 cmp     bp,dx
  144.                 jb      wrap_b2_ok
  145.                 sub     bp,dx
  146. wrap_b2_ok:     mov     MapInfo.OffY2,bp
  147.  
  148.                 mov     dx,MapInfo.Ht
  149.                 mov     bp,MapInfo.WrapY
  150.                 dec     bp
  151.                 jg      wrap_b3_ok
  152.                 add     bp,dx
  153. wrap_b3_ok:     mov     MapInfo.WrapY,bp
  154.  
  155.                 call    update_bottom
  156.                 mov     ax,cs:ScrollPosY
  157.                 jmp     calculate       ; Jump down to calc new offsets
  158.  
  159. EVEN    ; Same for top
  160. wrap_t:         sub     cs:upper_left,(SQUARE_HEIGHT * VIRTUAL_WIDTH) / 4
  161.                 add     ax,SQUARE_HEIGHT * VIRTUAL_WIDTH
  162.                 mov     cs:ScrollPosY,ax
  163.  
  164.                 mov     bp,MapInfo.OffY1
  165.                 mov     dx,MapInfo.Extent
  166.                 sub     bp,MapInfo.Wid
  167.                 cmp     bp,dx
  168.                 jb      wrap_t1_ok
  169.                 add     bp,dx
  170. wrap_t1_ok:     mov     MapInfo.OffY1,bp
  171.  
  172.                 mov     bp,MapInfo.OffY2
  173.                 sub     bp,MapInfo.Wid
  174.                 cmp     bp,dx
  175.                 jb      wrap_t2_ok
  176.                 add     bp,dx
  177. wrap_t2_ok:     mov     MapInfo.OffY2,bp
  178.  
  179.                 mov     bp,MapInfo.WrapY
  180.                 mov     dx,MapInfo.Ht
  181.                 inc     bp
  182.                 cmp     bp,dx
  183.                 jbe     wrap_t3_ok
  184.                 sub     bp,dx
  185. wrap_t3_ok:     mov     MapInfo.WrapY,bp
  186.  
  187.                 call    update_top
  188.                 mov     ax,cs:ScrollPosY
  189.                 jmp     calculate       ; Jump down to calc new offsets
  190.  
  191. EVEN
  192. align_mask_table DB     11h,22h,44h,88h
  193. calculate:
  194.         ; Calculate the scroll offset
  195.         ; AX already = ScrollPosY
  196.                 add     ax,cs:ScrollPosX        ;Now AX = scroll offset
  197.  
  198.         ; Calculate the plane alignment
  199.                 mov     bl,al
  200.                 and     bx,0003h
  201.                 mov     cs:DrawPage.Alignment,bl
  202. ;               mov     bl,cs:align_mask_table[bx]
  203. ;               mov     cs:DrawPage.AlignmentMask,bl
  204.  
  205.         ; Now we don't need Scroll Offset on a pixel level any more,
  206.         ; so shift it to a byte level (/4) and store it away.
  207.                 shr     ax,2
  208.                 mov     cs:DrawPage.ScrollOffset,ax
  209.  
  210.         ; Calculate the actual upper left corner address
  211.                 mov     si,cs:DrawPage.Address
  212.                 add     si,cs:upper_left
  213.                 mov     cs:DrawPage.UpperLeftAddress,si
  214.  
  215.         ; And the map offset:
  216.                 mov     bx,MapInfo.WrapX
  217.                 mov     cs:DrawPage.MapPosX,bx
  218.                 mov     di,MapInfo.WrapY
  219.                 mov     cs:DrawPage.MapPosY,di
  220.  
  221.                 mov     cs:DrawPage.Valid,1
  222.                 cmp     cs:BlankPage.Valid,0
  223.                 je      no_catch_up
  224.  
  225.         ; Lastly, update dirty area (if any) on blank page.
  226.         ; BP still contains the draw page's mapoffset.
  227.                 sub     bx,cs:BlankPage.MapPosX
  228.                 sub     di,cs:BlankPage.MapPosY
  229.                 jnz     yes_catch_up
  230.                 cmp     bx,0
  231.                 jnz     yes_catch_up
  232.         ; No catchup necessary -- return.
  233. no_catch_up:    ret
  234.  
  235. ;; Okay, this stuff is a mess.  I've registerized everything except
  236. ;; for the video data itself.  I'll try to comment it best I can.
  237. EVEN
  238. yes_catch_up:
  239.         ; First, switch into full-copy mode.  This means latching the
  240.         ; bit mask as coming entirely from the local 32-bit registers
  241.         ; and then setting the map mask to write to all 4 planes.  This
  242.         ; is Mode X's greatest advantage, when you can do it!  It
  243.         ; provides a 2x speedup or so...
  244.                 mov     dx,SC_INDEX     ; Select Sequencer input
  245.                 mov     ax,0F02h
  246.                 out     dx,ax           ; set map mask = all bits
  247.  
  248.                 mov     dx,GC_INDEX
  249.                 mov     ax,ALL_COPY_BITS
  250.                 out     dx,ax
  251.  
  252.                 JKEYNP  kB,isntbp
  253. isbp:           nop
  254. isntbp:
  255.         ; Next, calculate the amount to catch up the top/bottom rows
  256.         ; If we just wrapped over the edge, it is possible that the
  257.         ; distance traveled will be as high as MapInfo.Ht - 1.  So,
  258.         ; in the fashion of signed numbers, if the number is greater
  259.         ; than MapInfo.Ht / 2, we take it to mean negative.  To convert
  260.         ; it to signed, we have to shift it into the proper range.  But
  261.         ; if it's less than MapInfo.Ht / 2, then it's okay as it is.
  262.                 mov     ax,di
  263.                 cmp     ax,0
  264.                 je      y_mod
  265.  
  266.                 mov     cx,MapInfo.Ht
  267.                 cwd             ; DX = -1 or 0 based on AX's sign.
  268.                 and     dx,cx   ; DX = Ht or 0
  269.                 add     ax,dx   ; AX = 0 ... Ht (unsigned)
  270.  
  271.                 mov     di,ax
  272.                 shl     di,1
  273.                 cmp     di,cx
  274.                 jb      y_signed
  275.                 sub     ax,cx
  276. y_signed:       neg     ax
  277.  
  278.         ; Find DI MOD MapInfo.Wid, and then convert to it into virtual
  279.         ; coordinates from map offset coordinates.
  280.         ; This routine also calculates BP, which will be used as a loop
  281.         ; counter to determine how many rows to draw on the left/right
  282.         ; column copy.
  283. y_mod:          mov     bp,ax
  284.                 cwd
  285.                 add     bp,dx
  286.                 xor     bp,dx
  287.                 shl     bp,3            ; BP = (SQUARE_HEIGHT / 2) * dX
  288.                 mov     di,cs:MultVirtWidth[bp] ; Use multiplication table
  289.                 add     di,dx                   ; to calculate new DI, then
  290.                 xor     di,dx                   ; restore the sign.
  291.                 sub     bp,VIRTUAL_HEIGHT / 2
  292.         ; Out:  DI = # of pixels traveled,
  293.         ;       BP = (VIRTUAL_HEIGHT - # of rows) / 2
  294.  
  295.         ; Change BX (delta-x) to signed from unsigned, store in AX
  296.                 mov     ax,bx
  297.                 mov     cx,MapInfo.Wid
  298.                 cwd
  299.                 and     dx,cx   ; DX = Wid or 0
  300.                 add     ax,dx   ; AX = 0 ... Wid
  301.  
  302.                 mov     bx,ax
  303.                 shl     bx,1
  304.                 cmp     bx,cx
  305.                 jb      x_signed
  306.                 sub     ax,cx
  307. x_signed:
  308.  
  309.         ; The following is an optimization which would slow down on
  310.         ; normal memory, but I believe it will be okay on VGA memory,
  311.         ; which is so incredibly slow.  Basically, I've replaced all
  312.         ; "rep movsb"'s with a loop that first calculates "bx = di - si",
  313.         ; and then loops performing "mov ds:[si],es:[si+bx]".  Why?
  314.         ; Because of several reasons, none of which I'm sure actually
  315.         ; help out, but they do make for smaller code.  1)  It means that
  316.         ; I only have to maintain SI, and "DI" is maintained automatically
  317.         ; (because DI - SI should remain constant).  2)  Don't have to
  318.         ; calculate DS.  Not much gain here.  3)  Because I'd already
  319.         ; unrolled the loops, and the "rep movsb"'s had become instead
  320.         ; "mov al, ds:[si] / mov es:[di], al / mov al, ds:[si + 1] /
  321.         ; mov es:[di + 1],al ... etc ... add si, 4 / add di, 4".  In
  322.         ; other words, I wasn't using MOVSB anyway.  The only advantage
  323.         ; I can see in MOVSB is that it doesn't have to store the answer
  324.         ; in AL so it could be slightly faster.  By unrolling the loops,
  325.         ; I'd already made up for that, I think.  4)  Normally, using
  326.         ; [SI + BX + 1] would incur a penalty of an additional clock
  327.         ; cycle (because it has to add two indexs + an offset).  But
  328.         ; the VGA memory and the '86 CPU can multi-task, and the VGA
  329.         ; is very slow.  So by the time the VGA is ready to write the
  330.         ; next byte, the one extra clock cycle has already passed.
  331.         ;
  332.         ; Am I right?  Does this make things faster?  I have no idea.
  333.         ; I haven't bothered to check both ways.  Please let me know
  334.         ; if I've missed something important...
  335.         ;
  336.         ; Here's the calculation of BX.  SI is already set.
  337.                 ; si already = DrawPage.UpperLeftAddress
  338.                 mov     bx,cs:BlankPage.Address
  339.                 sub     bx,cs:DrawPage.Address
  340.  
  341.         ; Now, converts SI into "1/4" units.  I do all the calculations
  342.         ; in "1/4" scale and then scale back up, mostly because it saved
  343.         ; me some instructions elsewhere.
  344.                 shr     si,2
  345.         ; Stores this value of SI.  This will be restored after doing
  346.         ; the top/bottom copying.
  347.                 mov     dx,si
  348.  
  349.         ; Check if it's necessary to catch up the top or bottom.
  350. catchup_tb:     cmp     di,0
  351.                 je      catchup_tb_end
  352.                 jl      catchup_t
  353. catchup_b:      ; COPY BOTTOM
  354.         ; Move SI to point at the bottom of the screen - # of rows
  355.         ; to update.
  356.                 add     si,((VIRTUAL_WIDTH * VIRTUAL_HEIGHT) / 4) / 4
  357.                 sub     si,di
  358.                 jmp     copy_tb
  359. catchup_t:      ; COPY_TOP
  360.         ; Leave SI, but add to the "pushed" value of SI the number of
  361.         ; rows that will be drawn.  This prevents overlap between top
  362.         ; and right/left when moving diagonally.  Also, DI = |DI|
  363.                 neg     di
  364.                 add     dx,di
  365.  
  366.         ; Now do the actual copying.  Shifts SI back into scale "1",
  367.         ; then performs an unrolled loop to copy the entire virtual
  368.         ; width * # of pixel rows.  Since DI is already in "1/4" scale,
  369.         ; it is only decremented once for each four pixels drawn.
  370. copy_tb:        shl     si,2
  371. copy_tb_loop:   mov     cl,es:[si]
  372.                 mov     es:[si+bx],cl
  373.                 mov     cl,es:[si+1]
  374.                 mov     es:[si+bx+1],cl
  375.                 mov     cl,es:[si+2]
  376.                 mov     es:[si+bx+2],cl
  377.                 mov     cl,es:[si+3]
  378.                 mov     es:[si+bx+3],cl
  379.                 add     si,4
  380.                 dec     di
  381.                 jnz     copy_tb_loop
  382. catchup_tb_end:
  383.  
  384.         ; Next, check to see if it's necessary to draw the right or
  385.         ; the left side.
  386. catchup_rl:     cmp     ax,0
  387.                 je      catchup_rl_end
  388.                 jg      catchup_l
  389. catchup_r:      ; COPY RIGHT
  390.         ; Adds to the "pushed" SI the width of the screen, minus
  391.         ; the number of rows to be drawn.
  392.                 neg     ax
  393.                 add     dx,(VIRTUAL_WIDTH / 4) / 4
  394.                 sub     dx,ax
  395. catchup_l:      ; COPY LEFT (or nothing)
  396.  
  397.         ; Does the actual copying.  First pops SI from its stored value
  398.         ; and shifts it back into scale "1"
  399. copy_rl:        mov     si,dx
  400.                 shl     si,2
  401.  
  402.         ; This is a loop over BP -- which has already been set as
  403.         ; VIRTUAL_HEIGHT - (# of bytes drawn in vertical update)
  404.         ; Again, this loop is unrolled such that it does two rows @
  405.         ; 4 bytes each with every iteration.
  406.         ; This LEA instruction is just a quick MOV DI, SI + 2 *y
  407.         ; DI is used to push the next value of SI for each iteration
  408.         ; of the loop.
  409. copy_rl_loop:   lea     di,[si + 2*(VIRTUAL_WIDTH/4)]
  410.                 mov     cx,ax
  411. copy_rl_col:    mov     dl,es:[si]
  412.                 mov     es:[si+bx],dl
  413.                 mov     dl,es:[si+1]
  414.                 mov     es:[si+bx+1],dl
  415.                 mov     dl,es:[si+2]
  416.                 mov     es:[si+bx+2],dl
  417.                 mov     dl,es:[si+3]
  418.                 mov     es:[si+bx+3],dl
  419.                 mov     dl,es:[si+VIRTUAL_WIDTH/4]
  420.                 mov     es:[si+bx+VIRTUAL_WIDTH/4],dl
  421.                 mov     dl,es:[si+VIRTUAL_WIDTH/4+1]
  422.                 mov     es:[si+bx+VIRTUAL_WIDTH/4+1],dl
  423.                 mov     dl,es:[si+VIRTUAL_WIDTH/4+2]
  424.                 mov     es:[si+bx+VIRTUAL_WIDTH/4+2],dl
  425.                 mov     dl,es:[si+VIRTUAL_WIDTH/4+3]
  426.                 mov     es:[si+bx+VIRTUAL_WIDTH/4+3],dl
  427.                 add     si,4
  428.                 dec     cx
  429.                 jnz     copy_rl_col
  430.                 mov     si,di           ; SI = pop (SI + VIRTUAL_WIDTH/4)
  431.                 inc     bp              ; (BP is negative, so INC it)
  432.                 jnz     copy_rl_loop
  433. catchup_rl_end:
  434.  
  435.         ; Switch back to all-draw mode.
  436.                 mov     dx,GC_INDEX
  437.                 mov     ax,ALL_DRAW_BITS
  438.                 out     dx,ax
  439.                 ret
  440. Scroll          ENDP
  441.